#ifndef BOOTLOADER_STM32_QSPI_H
#define BOOTLOADER_STM32_QSPI_H

#include "SPIFlash.h"

#ifdef ARDUINO

#include <Arduino.h>

#endif
#ifdef STM32CUBE

#include <stm32h7xx_hal.h>
#include <stm32h7xx_hal_qspi.h>

#endif

class QUAD_SPI : public SPIFlash {
public:
    int init() override;

    int readBytes(uint32_t addr, void *buf, uint16_t len) override;

    int readRamBytes(uint32_t addr, void *buf, uint16_t len);

    int writeBytes(uint32_t addr, const void *buf, uint16_t len) override;

    int writeRamBytes(uint32_t addr, const void *buf, uint16_t len);

    int enter_quad_mode(uint8_t);

    int enter_ram_quad_mode();

    int blockErase4K(uint32_t address) override;

    void chipErase() override;

    uint8_t QSPI_ResetMemory();

    //
    //    uint16_t readDeviceId();
    //
    //    uint8_t *readUniqueId();
    //
    //    void disableGlobalBlockProtect();
    //
    //    void sleep();
    //
    //    void wakeup();
    //
    //    void end();
    //
    //    uint32_t capacity();
    //
    //    uint32_t blockSize();
    //
    //    void self_test();
    uint8_t QSPI_WriteEnable();

    uint8_t QSPI_AutoPollingMemReady(uint32_t Timeout);

    int EnableMemoryMappedMode();

    int DisableMemoryMappedMode();

    uint32_t alt_bytes_mode = QSPI_ALTERNATE_BYTES_4_LINES;
    uint32_t alt_bytes_size = QSPI_ALTERNATE_BYTES_8_BITS;
    uint32_t alt_bytes = 0x00;
    uint32_t dummy_cycles = 4;
    uint32_t alt_bytes_cycles = 0;
#define READ_ID_CMD 0x90
#define DUAL_READ_ID_CMD 0x92
#define QUAD_READ_ID_CMD 0x94
    static constexpr uint8_t JEDEC_ID = 0x9F;

    static constexpr uint8_t WRITE_ENABLE = 0x06;
    static constexpr uint8_t WRITE_DISABLE = 0x04;

    static constexpr uint8_t READ_STATUS_REGISTER = 0x05;
    static constexpr uint8_t WRITE_STATUS_REGISTER = 0x01;

    static constexpr uint8_t SECTOR_ERASE = 0x20;
    static constexpr uint8_t BLOCK_32K_ERASE = 0x52;
    static constexpr uint8_t BLOCK_64K_ERASE = 0xD8;
    static constexpr uint8_t CHIP_ERASE = 0xC7;

    static constexpr uint8_t RESET_ENABLE = 0x66;
    static constexpr uint8_t RESET_DEVICE = 0x99;

    static constexpr uint8_t POWER_DOWN = 0xB9;
    static constexpr uint8_t RELEASE_POWER_DOWN = 0xAB;

    static constexpr uint8_t PAGE_PROGRAM = 0x02;
    static constexpr uint8_t QUAD_PAGE_PROGRAM = 0x32;

    static constexpr uint8_t READ_DATA = 0x03;
    static constexpr uint8_t FAST_READ_DATA = 0x0B;
    static constexpr uint8_t FAST_READ_QUAD_OUT = 0x6B;
    static constexpr uint8_t QUAD_INOUT_FAST_READ_CMD = 0xEB;

    static constexpr uint32_t one_sec = 1000;
    static constexpr uint32_t TIMEOUT = 10;
    static constexpr uint32_t WRITE_TIMEOUT = 50;
    static constexpr uint32_t ERASE_TIMEOUT = 2 * one_sec;
    static constexpr uint32_t CHIP_ERASE_TIMEOUT = 200 * one_sec;
    // Winbond specific commands
    static constexpr uint8_t VOLATILE_SR_WRITE_ENABLE = 0x50;
    static constexpr uint8_t READ_STATUS_REGISTER_2 = 0x35;
    static constexpr uint8_t WRITE_STATUS_REGISTER_2 = 0x31;
    static constexpr uint8_t READ_STATUS_REGISTER_3 = 0x15;
    static constexpr uint8_t WRITE_STATUS_REGISTER_3 = 0x11;
    static constexpr uint8_t ENTER_QUAD_MODE = 0x38;
    static constexpr uint8_t SET_READ_PARAMETERS = 0xC0;

    int exit_quad();

    uint8_t QSPI_Transmit(uint8_t *buf);

    uint8_t QSPI_Receive(uint8_t *buf);

    void QSPI_Send_CMD(uint8_t cmd, uint32_t addr, uint8_t mode, uint8_t dmcycle, uint32_t);

    void NORFLASH_Write_SR(uint8_t regno, uint8_t sr);

    uint8_t NORFLASH_ReadSR(uint8_t regno);

    void NORFLASH_Wait_Busy();

    int erasePage(uint32_t adr, uint32_t pageNrs);

    void *lock{};
    enum {
        D1A1C1,
        D4A4C1
    };
    uint8_t quad_mode{};
};

#endif // BOOTLOADER_STM32_QSPI_H
